package com.nycreativity.elecreativity.controller.ijpay.paypal;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.paypal.PayPalApi;
import com.ijpay.paypal.PayPalApiConfig;
import com.ijpay.paypal.PayPalApiConfigKit;
import com.ijpay.paypal.accesstoken.AccessToken;
import com.ijpay.paypal.accesstoken.AccessTokenKit;
import com.nycreativity.elecreativity.config.ijpay.paypal.CustomPayPalApiConfigKit;
import com.nycreativity.elecreativity.controller.ijpay.AbstractIJPayController;
import com.nycreativity.elecreativity.entity.ResultData;
import com.nycreativity.elecreativity.entity.cons.CommonURLConstants;
import com.nycreativity.elecreativity.entity.enums.OrderStatusEnum;
import com.nycreativity.elecreativity.entity.enums.PaymentMethodEnum;
import com.nycreativity.elecreativity.entity.enums.ResultStatusEnum;
import com.nycreativity.elecreativity.entity.po.Order;
import com.nycreativity.elecreativity.service.OrderService;
import com.nycreativity.elecreativity.util.FCConvertUtil;
import com.nycreativity.elecreativity.util.GetUrlParameter;
import com.nycreativity.elecreativity.util.URLUtil;
import com.nycreativity.elecreativity.util.usersession.UserContext;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.Nullable;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.Objects;

@RestController
@RequestMapping(CommonURLConstants.PAYPAL_PREFIX)
@Api(tags = "PayPal支付相关接口")
@Slf4j
public class PayPalIJPayController implements AbstractIJPayController<PayPalApiConfig> {

    @Resource
    private CustomPayPalApiConfigKit customPayPalApiConfigKit;

    /**
     * 确认支付后的回调地址
     */
    private final static String RETURN_URL = CommonURLConstants.PAYPAL_PREFIX + "/return";

    /**
     * 取消支付后的回调地址
     */
    private final static String CANCEL_URL = CommonURLConstants.PAYPAL_PREFIX + "/cancel";

    @Resource
    OrderService orderService;

    @GetMapping(value = "/getAccessToken")
    @ApiOperation(value = "获取访问token", hidden = true)
    public AccessToken getAccessToken() {
        try {
            getApiConfig();
            return AccessTokenKit.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @GetMapping(value = "/createOrder")
    @ApiOperation("发起支付")
    @ApiImplicitParam(name = "orderId", value = "订单id")
    public ResultData<String> createOrder(Long orderId, HttpServletRequest request) {
        try {
            PayPalApiConfig config = getApiConfig();
            //参数请求参数文档 https://developer.paypal.com/docs/api/orders/v2/#orders_create
            Order order = orderService.getById(orderId);
            if (order == null) {
                log.error("订单编号有误：" + orderId);
                return ResultData.FAILED("订单编号有误: " + orderId);
            } else if (!Objects.equals(order.getStatus(), OrderStatusEnum.WAITING_FOR_PAYMENT.getStatus())) {
                log.error("订单状态异常：" + orderId);
                return ResultData.FAILED("订单状态异常: " + orderId);
            }
            log.info("订单PayPal支付 > " + orderId);
            double amountUsd = FCConvertUtil.convert("CNY", "USD", order.getTotalAmount().doubleValue());
            order.setAmountUsd(new BigDecimal(amountUsd));
            String data = new PayPalRequestData.CreateDataBuilder()
                    .returnUrl(URLUtil.getBaseURl(request).concat(RETURN_URL))
                    .cancelUrl(URLUtil.getBaseURl(request).concat(CANCEL_URL))
                    .amount(order.getAmountUsd().doubleValue())
                    .build();
            log.info(data);
            IJPayHttpResponse resData = PayPalApi.createOrder(config, data);
            log.info(resData.toString());
            if (resData.getStatus() == 201) {
                String resultStr = resData.getBody();
                JSONObject jsonObject = JSONUtil.parseObj(resultStr);
                JSONArray links = jsonObject.getJSONArray("links");
                for (int i = 0; i < links.size(); i++) {
                    JSONObject item = links.getJSONObject(i);
                    String rel = item.getStr("rel");
                    String href = item.getStr("href");
                    if ("approve".equalsIgnoreCase(rel)) {
                        // 更新订单的支付id信息
                        order.setPaymentInnerId((String) GetUrlParameter.getParameter(href).get("token"));
                        order.setPaymentMethod(PaymentMethodEnum.PayPalPay.getCode());
                        order.modify();
                        order.updateById();
                        return ResultData.SUCCESS("PayPal支付订单创建成功", href);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ResultData.FAILED("PayPal支付订单创建失败");
        }
        return ResultData.FAILED("PayPal支付订单创建失败");
    }

    @GetMapping(value = "/queryOrder")
    @ApiOperation(value = "查询支付订单", hidden = true)
    public ResultData<JSONObject> queryOrder(@RequestParam("id") String id) {
        try {
            PayPalApiConfig config = getApiConfig();
            IJPayHttpResponse response = PayPalApi.queryOrder(config, id);
            log.info(response.toString());
            if (response.getStatus() == 200) {
                return ResultData.SUCCESS("成功", JSONUtil.parseObj(response.getBody()));
            } else {
                return ResultData.FAILED("接口请求错误码为：" + response.getStatus());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 确认订单付款(确认订单付款后, 买家账号才会发生扣款)
     *
     * @param id 订单号 token
     * @return 捕获订单付款是否成功
     */
    private ResultData<Object> captureOrder(@RequestParam("id") String id) {
        Order order = orderService.getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getPaymentInnerId, id), false);
        if (order == null) {
            return ResultData.FAILED("支付失败, 支付链接可能已被刷新, 请重新发起支付");
        }
        PayPalApiConfig config = getApiConfig();
        IJPayHttpResponse response = PayPalApi.captureOrder(config, id, "");
        log.info(response.toString());
        if (response.getStatus() == 200 || response.getStatus() == 201) {
            return ResultData.SUCCESS();
        } else {
            return ResultData.FAILED("接口请求错误码为：" + response.getStatus());
        }
    }

    @GetMapping(value = "/refund")
    @ApiOperation("发起支付退款")
    @ApiImplicitParam(name = "orderId", value = "订单id")
    public ResultData<Object> refund(String orderId) {
        Order order = orderService.getById(orderId);
        // 发起退款请求前对目标订单进行检测
        ResultData<Object> failed = checkOrderRefund(order);
        if (failed != null) {
            return failed;
        }

        PayPalApiConfig config = getApiConfig();
        String data = new PayPalRequestData.RefundDataBuilder()
                .amount(order.getAmountUsd().doubleValue())
                .build();
        log.info("refund data: {}", data);
        IJPayHttpResponse response = PayPalApi.refund(config, queryOrderCaptureId(order.getPaymentInnerId()), data);
        log.info(response.toString());
        if (response.getStatus() == 201) {
            order.setStatus(OrderStatusEnum.HAS_RETURNED.getStatus());
            order.modify();
            order.updateById();
            return ResultData.SUCCESS("退款成功");
        } else {
            return ResultData.FAILED("退款失败, 接口请求信息为：" + JSONUtil.parseObj(response.getBody()));
        }
    }

    @Nullable
    private ResultData<Object> checkOrderRefund(Order order) {
        if (order == null) {
            return ResultData.FAILED("退款请求发起失败, 订单不存在");
        }
        if (!Objects.equals(order.getCreator(), UserContext.require().id)) {
            return ResultData.FAILED("当前用户不存该笔订单");
        }
        if (Objects.equals(order.getStatus(), OrderStatusEnum.WAITING_FOR_PAYMENT.getStatus())) {
            return ResultData.FAILED("退款请求发起失败, 订单未支付");
        }
        if (Objects.equals(order.getStatus(), OrderStatusEnum.HAS_RETURNED.getStatus())) {
            return ResultData.FAILED("退款请求发起失败, 订单已退款");
        }
        return null;
    }

    @GetMapping(value = "/return")
    @ApiOperation(value = "支付界面确认支付后跳转的链接", hidden = true)
    public ResultData<Object> returnUrl(HttpServletRequest request, HttpServletResponse response) {
        try {
            String token = request.getParameter("token");
            log.info("token: {}", token);
            // 确认订单信息, 注意: 订单信息只有在确认后才会发生扣款
            ResultData<Object> captureResult = captureOrder(token);
            if (captureResult.getStatus().equals(ResultStatusEnum.SUCCESS.getStatus())) {
                // 保存支付信息
                updateOrder(token);
                // 网页重定向到前端固定网址
                response.sendRedirect("/paypal_success.html");
                return null;
            } else {
                ResultData.FAILED("付款失败, 请重试");
            }
        } catch (Exception e) {
            e.printStackTrace();
            ResultData.FAILED("付款失败, 请重试");
        }
        return ResultData.FAILED("付款失败, 请重试");
    }

    @GetMapping(value = "/cancel")
    @ApiOperation(value = "支付界面取消支付后跳转的链接", hidden = true)
    public ResultData<Object> cancelUrl() {
        return ResultData.SUCCESS("取消支付: 订单支付取消或订单重复支付");
    }

    @Override
    public PayPalApiConfig getApiConfig() {
        return PayPalApiConfigKit.getApiConfig(customPayPalApiConfigKit.getPayPalApiConfigs().get(0).getClientId());
    }

    /**
     * 查询支付捕获的id
     *
     * @return 支付捕获的id
     */
    private String queryOrderCaptureId(String id) {
        JSONObject order = queryOrder(id).getData();
        if (order != null) {
            JSONObject payment = order
                    .getJSONArray("purchase_units")
                    .getJSONObject(0)
                    .getJSONObject("payments")
                    .getJSONArray("captures")
                    .getJSONObject(0);
            return payment.get("id", String.class);
        }
        return "null";
    }

    /**
     * 更新订单支付成功的状态
     *
     * @param payPalToken 支付的orderId (token)
     */
    private void updateOrder(String payPalToken) {
        Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getPaymentInnerId, payPalToken), false);
        // 如果还是待付款的状态则更新
        if (Objects.equals(order.getStatus(), OrderStatusEnum.WAITING_FOR_PAYMENT.getStatus())) {
            order.setStatus(OrderStatusEnum.WAITING_FOR_DELIVER.getStatus());
            order.modify();
            order.updateById();
        }
    }
}
